/**
 * 
 */
package com.wy2.generator;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Map;

import cn.xo68.core.date.DateTime;
import cn.xo68.core.util.StringTools;
import com.wy2.generator.event.OutputFileEvent;
import com.wy2.generator.event.OutputFileEventArgs;
import com.wy2.generator.exception.GeneratorInitException;
import com.wy2.generator.exception.GeneratorOutputException;
import com.wy2.generator.resource.Messages;
import com.wy2.generator.util.FreemarkerUtils;

import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.StringTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;

/**
 * 抽象生成器
 * @author nnsword
 * @date 2017-12-15 00:37:53
 *
 */
public abstract class AbstractGenerator {
	/**
	 * 模板目录
	 */
	private final File templateDirectory;
	/**
	 * 输出根目录
	 */
	private final File outputRootDirectory;
	/**
	 * 模板编码
	 */
	private final String templateEncoding;
	/**
	 * 标识模板字符串
	 */
	private final String markerTemplateString;
	
	/**
	 * freemarker模板配置
	 */
	private Configuration templateConfiguration;
	/**
	 * 输出文件事件
	 */
	private OutputFileEvent outputFileEvent;
	
	
	
	
	/**
	 * @return the templateDirectory
	 */
	public File getTemplateDirectory() {
		return templateDirectory;
	}

	/**
	 * @return the outputRootDirectory
	 */
	public File getOutputRootDirectory() {
		return outputRootDirectory;
	}

	/**
	 * @return the templateEncoding
	 */
	protected String getTemplateEncoding() {
		return templateEncoding;
	}

	/**
	 * @return the templateConfiguration
	 */
	protected Configuration getTemplateConfiguration() {
		return templateConfiguration;
	}
	
	


	/**
	 * @return the outputFileEvent
	 */
	public OutputFileEvent getOutputFileEvent() {
		return outputFileEvent;
	}

	/**
	 * @param outputFileEvent the outputFileEvent to set
	 */
	public void setOutputFileEvent(OutputFileEvent outputFileEvent) {
		this.outputFileEvent = outputFileEvent;
	}

	/**
	 * 初始化模板配置，并加载自定义标签的字符串模板
	 * @date 2017-12-17 22:40:14
	 * @param stringTemplates
	 * @throws GeneratorInitException
	 */
	public void initTemplateConfig(String...stringTemplates) throws GeneratorInitException {
		templateConfiguration = new Configuration(Configuration.VERSION_2_3_22);		
		try {			
			TemplateLoader fileTemplateLoader = new FileTemplateLoader(this.templateDirectory);
			StringTemplateLoader stringLoader = new StringTemplateLoader();
			stringLoader.putTemplate(this.markerTemplateString, this.markerTemplateString);
			String templateContent;
			for (String stringTemplate : stringTemplates) {
				templateContent=FreemarkerUtils.customTagConvert(FreemarkerUtils.stringTemplateDecode(stringTemplate));
				stringLoader.putTemplate(stringTemplate,templateContent); 
			}
			MultiTemplateLoader multiTemplateLoader = new MultiTemplateLoader(new TemplateLoader[] { fileTemplateLoader,stringLoader  });
			templateConfiguration.setTemplateLoader(multiTemplateLoader);
			
			templateConfiguration.setObjectWrapper(new DefaultObjectWrapper(Configuration.VERSION_2_3_22));
			templateConfiguration.setDefaultEncoding(this.templateEncoding);
			//设计异常处理器  
			//templateConfiguration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
			config(this.templateConfiguration);
		}catch(IOException e) {
			throw new GeneratorInitException(Messages.getString("GeneratorInitException.0"), e);
		}
	}
	
	/**
	 * 设置模板配置属性
	 * @date 2017-12-15 01:17:00
	 * @param cfg
	 */
	protected abstract void config(Configuration cfg);
	
	/**
	 * 获得 标识字符串
	 * @date 2017-12-22 00:40:23
	 * @param data
	 * @return
	 */
	public String getMarkerString(Map<String,Object> data) {
		return output(markerTemplateString, data, getTemplateEncoding());
	}
	

	/**
	 * 输出模板合成结果
	 * @date 2017-12-20 22:04:48
	 * @param templatePath  模板相对路径 或 字符串模板的编号
	 * @param data  传入数据
	 * @param writer
	 * @throws GeneratorOutputException
	 */
	public void outputWriter(String templatePath,Map<String,Object> data,Writer writer)  throws GeneratorOutputException{
		Template temp;
		try {
			temp = getTemplateConfiguration().getTemplate(templatePath);
			temp.process(data, writer);
			writer.flush();
		} catch (TemplateNotFoundException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.0"), e);
		} catch (MalformedTemplateNameException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.0"), e);
		} catch (ParseException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.0"), e);
		} catch (TemplateException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.3"), e);
		} catch (IOException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.2"), e);
		}
	}
	/**
	 * 输出模板合成结果
	 * @date 2017-12-20 22:05:14
	 * @param templatePath   模板相对路径 或 字符串模板的编号
	 * @param data  传入数据
	 * @throws GeneratorOutputException
	 */
	public void outputConsole(String templatePath,Map<String,Object> data)  throws GeneratorOutputException {
		Writer out = new OutputStreamWriter(System.out);
		outputWriter(templatePath, data, out);
	}
	/**
	 * 输出模板合成结果
	 * @date 2017-12-20 22:04:00
	 * @param templatePath  模板相对路径 或 字符串模板的编号
	 * @param data 传入数据
	 * @param relativeOutputPath
	 * @param override
	 * @param encoding
	 * @throws GeneratorOutputException
	 */
	public GenerateResult outputFile(String templatePath,Map<String,Object> data,String relativeOutputPath,Boolean override,String encoding) throws GeneratorOutputException {
		if(StringTools.isEmpty(relativeOutputPath)) {
			return new GenerateResult(output(this.markerTemplateString, data, encoding), relativeOutputPath, false);
		}
		if(relativeOutputPath.contains("\\")) {
			relativeOutputPath=relativeOutputPath.replace("\\", "/");
		}
		if(relativeOutputPath.startsWith("/")) {
			relativeOutputPath=relativeOutputPath.substring(1);
		}
		
		if(outputFileEvent!=null) {
			outputFileEvent.begin(new OutputFileEventArgs(output(this.markerTemplateString, data, encoding),this.outputRootDirectory.getAbsolutePath(),relativeOutputPath));
		}
		
		File outputFile=new File(this.outputRootDirectory, relativeOutputPath);
		
		
		
		//定义输入文件，默认生成在工程根目录  
		Template temp;
		Boolean success=true;
		Writer out = null;
		try {
			if(outputFile.exists() && !override) {
				return new GenerateResult(output(this.markerTemplateString, data, encoding), relativeOutputPath, true);
			}else if(!outputFile.exists()) {
				if(!outputFile.getParentFile().exists()) {
					outputFile.getParentFile().mkdirs();
				}
				outputFile.createNewFile();
			}
			out = new OutputStreamWriter(new FileOutputStream(outputFile,false), encoding);
			temp = getTemplateConfiguration().getTemplate(templatePath);
			data.put("now", DateTime.Now().toString());
			temp.process(data, out);
			out.flush();
			return new GenerateResult(output(this.markerTemplateString, data, encoding), relativeOutputPath, true);
		} catch (UnsupportedEncodingException e) {
			success=false;
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.1"), e);
		} catch (FileNotFoundException e) {
			success=false;
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.2"), e);
		} catch (GeneratorOutputException e) {
			success=false;
			throw e;
		} catch (IOException e) {
			success=false;
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.2"), e);
		} catch (TemplateException e) {
			success=false;
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.3"), e);
		}finally {
			if(outputFileEvent!=null) {
				outputFileEvent.finish(new OutputFileEventArgs(output(this.markerTemplateString, data, encoding),
						this.outputRootDirectory.getAbsolutePath(),
						relativeOutputPath,
						success));
			}
			try {
				if(out!=null) {
					out.close();
				}
			} catch (IOException e) {
				throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.2"), e);
			}
		}
		
	}
	/**
	 * 输出模板合成结果
	 * @date 2017-12-20 22:02:26
	 * @param templatePath 模板相对路径 或 字符串模板的编号
	 * @param data 传入模板的数据
	 * @param encoding 编码
	 * @return
	 * @throws GeneratorOutputException
	 */
	public String output(String templatePath,Map<String,Object> data,String encoding) throws GeneratorOutputException {
		Template temp;
		try {
			temp = getTemplateConfiguration().getTemplate(templatePath,encoding);
		} catch (TemplateNotFoundException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.0"), e);
		} catch (MalformedTemplateNameException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.0"), e);
		} catch (ParseException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.0"), e);
		} catch (IOException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.0"), e);
		}
		
		ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
		Writer out;
		try {
			out = new OutputStreamWriter(outputStream, encoding);
		} catch (UnsupportedEncodingException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.1"), e);
		}
		
		try {
			data.put("now", DateTime.Now().toString());
			temp.process(data, out);
		} catch (TemplateException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.2"), e);
		}catch ( IOException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.2"), e);
		}
		
		String str;
		try {
			str = new String(outputStream.toByteArray(), encoding);
		} catch (UnsupportedEncodingException e) {
			throw new GeneratorOutputException(Messages.getString("GeneratorOutputException.1"), e);
		}
		
		return str;
	}

	/**
	 * 
	 * @param templateDirectory
	 * @param outputRootDirectory
	 * @param markerTemplateString  生成标识的字符串模板
	 * @param templateEncoding
	 */
	public AbstractGenerator(File templateDirectory,File outputRootDirectory, String markerTemplateString, String templateEncoding) {
		super();
		this.templateDirectory = templateDirectory;
		this.outputRootDirectory=outputRootDirectory;
		this.markerTemplateString=markerTemplateString;
		this.templateEncoding = templateEncoding;
		
	}

	
	
}
